require 'parser/InvalidCall.rb'

# = Class ASTFuncCall
# Inherits from ASTExpression. Represents the implementation of an AST especifically designed
# for instructions that make function calls
# == Attributes inherited from ASTExpression
# node, children
# == Methods inherited from ASTExpression
# check(), run(), to_s_children(im)
class ASTFuncCall < ASTExpression
  
  # AST containing the name of the function being called
  attr_reader :name
  # Type of element returned by call
  attr_reader :type
  # Type of element returned by call
  attr_reader :value
  
  # Constructor of the class
  # === Parameters
  # * name: AST representing the name of the function being called
  # * params: AST containing the list of parameters being passed to the function
  def initialize name, params
    @name = name
    @children = params
  end

  # Method to evaluate the function call
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the parent AST
  # === Return value
  # nil if executed with success or an error message
  def eval parent_sym_table, func_table
    sym_func = func_table.find(@name.value)
    params = []
    for c in @children
      msg = c.eval parent_sym_table, func_table
      if nil != msg
        return msg
      end
      params.push c.value
    end
    msg = sym_func.execute params, func_table
    if nil != msg
      return msg + " (#{error_location})"
    end
    @value = sym_func.returned
    @type = (@value.kind_of?Vector) ? @value.type : @type
    nil
  end
  
  # Method to check the static correctitude of the function call
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the pare
  # * func_type: Type of valid return value
  # === Return value
  # nil if executed with success or an error message
  def check parent_sym_table, func_table, func_type
    sym_func = func_table.find @name.value
    if nil == sym_func
      return "Llamando a función inexistente '#{@name.value}' (#{error_location})"
    end
    if nil == @children
      return "Llamando sin lista (aunque fuera vacía) de parámetros '#{@name.value}' (#{error_location})"
    end

    # Check parameters
    for param in @children
      msg = param.check parent_sym_table, func_table, func_type
      if nil != msg
        return msg
      end
    end

    begin
      @type = sym_func.type @children
    rescue InvalidCall => err
      return err.msg + " (#{error_location})"
    end
    nil
  end

  # Returns the closest wrong token to the place were an error is generated
  def error_tk
    if nil != @name
      return @name
    end
    for e in @children
      tk = e.error_tk
      if nil != tk
        return tk
      end
    end
    nil
  end

  # Creates a 'String' representation of the AST with 'i' identation spaces
  # === Parameters
  # * i: Identation with which the AST should be printed with
  def to_s(i)
    im = i + @@c
    "(Llamada a función " + @name.value + "#{to_s_children(im)}\n#{i})"
  end
end